<?php
/**	CrugeFactory

	centraliza la creacion de instancias para lograr dar abstraccion al modelo OOP.

	es importante comprender que CrugeFactory es un punto para lograr la abstraccion
	solo factory accede a los modelos especificos, es decir, en ninguna parte
	se veran llamadas directas a clases del modelo, en cambio a interfaces si.

	dependencias:

		1. CrugeUtil
		2. package cruge.models
		3. package cruge.models

	como se accede al factory: (ejemplo)

		$value = CrugeFactory::get()->getConfiguredAuthMethodName();

	quien accede al factory:

		solo ciertas clases como CrugeUserManager o similares...

		EL USUARIO DE CRUGE NO DEBE ACCEDER A ESTA CLASE

	@author: Christian Salazar H. <christiansalazarh@gmail.com> @bluyell
	@copyright Copyright &copy; 2008-2012 Yii Software LLC
	@license http://www.yiiframework.com/license/
*/
class CrugeFactory {

	public static function get(){
		return CrugeUtil::factory();
	}


	/** devuelve el identificador activo de sesion pero bajo interpretacion de una clase
	que implemente a ICrugeSession

	*/
	public function getICrugeSession($idsession) {
		return CrugeSession::loadModel($idsession);
	}
	public function getICrugeSessionFindLastByUser($iduser) {
		return CrugeSession::findLast($iduser);
	}
	public function getICrugeSessionCreate($iduser,$durationMins) {
		return CrugeSession::create($iduser,$durationMins);
	}

	public function getICrugeSystemByName($systemName){
		$system = CrugeSystem::findSystem($systemName);
		if($system == null)
		{
			if(CrugeUtil::config()->debug == true){
				$sys = new CrugeSystem();
				$sys->name = 'default';
				$sys->sessionmaxdurationmins = 30;
				$sys->registerusingactivation = 0;
				$sys->registerusingcaptcha=0;
				$sys->registerusingterms=0;


				if($sys->insert()){
					return $sys;
				}
				else{
					throw new CrugeException("no se pudo crear el sistema de configuracion");
				}
			}
			else{
				/*
				  la causa mas comun de este error es que la tabla cruge_system esta vacia
				*/
				throw new CrugeException("no se pudo hallar el sistema de configuracion, quiza la tabla cruge_system esta vacia o ha indicado un identificador de sistema inexistente.");
			}
		}
		else
		return $system;
	}
	public function getICrugeSystemList(){
		return CrugeSystem::listModels();
	}

	public function getICrugeStoredUserList($param=array()){
		return CrugeStoredUser::listModels($param);
	}
	public function getICrugeStoredUserSortFieldNames(){
		return CrugeStoredUser::getSortFieldNames();
	}
	public function getNewICrugeStoredUserForSearch(){
		return new CrugeStoredUser('search');
	}
	public function getNewICrugeSessionForSearch(){
		return new CrugeSession('search');
	}
	public function getNewICrugeFieldForSearch(){
		return new CrugeField('search');
	}
	public function getICrugeFieldSortFieldNames(){
		return CrugeField::getSortFieldNames();
	}


	/*
		buscador multiproposito.  $id puede ser el iduser o el authkey, depende del flag boolean
		de seleccion: $boolFindByKey
	*/
	public function getICrugeStoredUserLoadModel($id,$booleanThrowsExceptionIfNull=true
		,$boolFindByKey=false){
		if($boolFindByKey == false){
			$model = CrugeStoredUser::loadModel($id);
		}else{
			$model = CrugeStoredUser::loadModel($id,'authkey');
		}
		if($booleanThrowsExceptionIfNull == true && $model==null)
			throw new CrugeException("usuario no encontrado");
		return $model;
	}
	public function getICrugeStoredUserNewModel(){
		$model = new CrugeStoredUser();
		$model->regdate = CrugeUtil::now();
		return $model;
	}
	/*  entrega la lista de campos, si se le da el usuario, va a pasar el valor del usuario
		al fieldvalue del campo.

		@returns una lista (array) de instancias de ICrugeField

		si $userInst es una instancia entonces renderiza cada campo con el valor asignado
		al usuario.  si $userInst es null solo se retorna la lista de campos sin renderizar valor
	*/
	public function getICrugeFieldListModels(/*ICrugeStoredUser*/ $userInst=null){
		$fields = CrugeField::listModels();

		foreach($fields as $f)
			$f->setFieldValue($f->predetvalue);

		// ahora, asocia cada campo con el valor del usuario seleccionado
		if($userInst != null){

			// busca los campos de este usuario
			$fieldvalues = CrugeFieldValue::listModels($userInst->getPrimaryKey());
			foreach($fields as $f)
				foreach($fieldvalues as $fv)
					if($f->idfield == $fv->idfield){
						$f->setFieldValue($fv->value==null ? "" : $fv->value);
						break;
					}

		}
		return $fields;
	}
	/*
		va a retornar un objeto que implementa a ICrugeFieldValue, el cual esta compuesto
		de dos objetos: el usuario y el campo.

		importante: si el campo solicitado no es hallado entonces sera creado para asegurar
		que siempre exista.
	*/
	public function getICrugeFieldValue(ICrugeStoredUser $user, ICrugeField $field){
		$model = CrugeFieldValue::loadModelBy($user->getPrimaryKey(),$field->getPrimaryKey());
		if($model == null)
			{
				// lo crea
				$model = new CrugeFieldValue();
				$model->iduser = $user->getPrimaryKey();
				$model->idfield = $field->getPrimaryKey();
				$model->value = "";
				if($model->save()){
					return $model;
				}else{
					Yii::log(
						 "error creando un nuevo CrugeFieldValue:\n"
						."iduser: {$user->getPrimaryKey()}\n"
						."idfield: {$field->getPrimaryKey()}\n"
						."fieldvalue: {$field->getFieldValue()}\n"
						."errorSummary:\n ".CHtml::errorSummary($model)."\n"
						,"error");
					return null;
				}
			}
		return $model;
	}
	public function getICrugeFieldLoadModel($id){
		return CrugeField::loadModel($id);
	}
	public function getICrugeFieldLoadModelByName($name){
		return CrugeField::loadModelByName($name);
	}
	public function getICrugeFieldCreate($fieldtype){
		$model = new CrugeField();
		$model->fieldtype = $fieldtype;
		$model->fieldname = CrugeTranslator::t("nuevocampo");
		$model->longname = CrugeTranslator::t("Nuevo Campo");
		$model->position = 0;
		$model->fieldsize = 20;
		$model->maxlength = 45;
		$model->required = false;
		$model->showinreports = false;
		return $model;
	}


	/*
		se trae un modulo de autenticacion hallado por su nombre (authName)
		retorna una instancia de este, siempre, sino una excepcion

		@returns instancia de ICrugeAuth buscada por su nombre y configurada en el sistema
	*/
	public function getICrugeAuthByName($authName){
		// verifica si el metodo esta disponible en config
		if(self::isAuthMethodAvailable($authName)) {
			foreach(self::_getAuthModes() as $mode)
				if($mode->authName() == $authName)
					return $mode;
			throw new CrugeException(
				"el atributo authmode no coincide con ningun modulo de autenticacion instalado");
		}
		else
		{
			throw new CrugeException(
				"el atributo authmode no esta definido como valido en config");
		}
	}
	/** busca un ICrugeStoredUser en el almacen de datos que cumpla con el identificador solicitado
		(usernameORemail) y que cumpla con la configuracion general del sistema

		@returns instancia que implementa a: ICrugeStoredUser o null
	*/
	public function getICrugeStoredUser($usernameORemail) {
		Yii::log(__METHOD__."\nusernameOrEmail=".$usernameORemail,"info");
		// verifica en la lista corta de setup del modulo a ver cuantos modos hay definidos
		//
		$n = count(CrugeUtil::config()->availableAuthModes);
		$model = null;
		if($n == 0){
			// seguramente se ha borrado o mal configurado el parametro de config/main.php
			// availableAuthModes.
			throw new CrugeException("no se definieron modos de autenticacion en config");
		}else{
			if($n == 1){
				// solo un metodo, username o email
				$key = CrugeUtil::config()->availableAuthModes[0];
				Yii::log(__METHOD__."\n buscando por '".$key."' a ".$usernameORemail,"info");
				$model = CrugeStoredUser::loadModel($usernameORemail,$key);
			}else{
				// son los dos metodos: username e email
				Yii::log(__METHOD__."\n buscando por -username- a ".$usernameORemail,"info");
				$model = CrugeStoredUser::loadModel($usernameORemail,'username');
				if($model == null){
					Yii::log(__METHOD__."\n buscando por -email- a ".$usernameORemail,"info");
					$model = CrugeStoredUser::loadModel($usernameORemail,'email');
				}
			}
			if($model == null){
				Yii::log(__METHOD__."\n **NO HALLADO** ".$usernameORemail,"warning");
			}else{
				Yii::log(__METHOD__."\n **USUARIO HA SIDO HALLADO** ".$usernameORemail,"info");
			}
			return $model;
		}
	}
	public function isAuthMethodAvailable($authName){
		foreach(CrugeUtil::config()->availableAuthMethods as $key=>$val){
			if($key == $authName)
				return true;
		}
		return false;
	}
	/*
		lee de CrugeModule el nombre del filtro de autenticacion a utilizar, por ahora
		solo un filtro se usa a la vez,a futuro se pretenden usar varios filtros, por eso es un array.

		retorna:  un string, el nombre del filtro de autenticacion, el nombre sera validado contra
		el valor que retorne el filtro usando la interfaz ICrugeAuth.authName()
	*/
	public function getConfiguredAuthMethodName(){
		foreach(CrugeUtil::config()->availableAuthMethods as $key=>$val){
				return $val;
		}
		return false;
	}



	/** entrega el filtro para procesar sesiones de usuario.

		si sessionfilter no es declarada en config, entonces se usa a _defaultSessionFilter.

		@returns instancia que implementa a ICrugeSessionFilter.
	*/
	public function getICrugeSessionFilter() {

		Yii::log(__CLASS__."\ngetICrugeSessionFilter\n","info");

		//$filterClass = $this->sessionfilter;
		//if($filterClass == null || $filterClass=='')
		$filterClass = CrugeUtil::config()->defaultSessionFilter;

		$filepath = Yii::getPathOfAlias($filterClass).".php";
		$className = CrugeUtil::getClassNameFromPhp($filepath);

		if(is_file($filepath)){
			if(!class_exists($className,false))
				require($filepath);
			if(class_exists($className,false)){
				Yii::log(__CLASS__."\ngetICrugeSessionFilter\nnew instance for: ".$className,"info");
				return new $className();
			}
			else{

				Yii::log("clase no hallada.".$className,"error");
				throw new CrugeException("clase no hallada. ver log.");
			}
		}
		else
		{
			Yii::log("ruta de clase es invalida:".$filepath,"error");
			throw new CrugeException("ruta de clase es invalida. ver log.");
		}
	}


	/*
		este metodo prepara y entrega los metodos de autenticacion
		normalmente, quien llama a este metodo es: CrugeUser en authenticate()

		las clases de autenticacion estan en models.auth y cada debe implementar
		la interfaz components.ICrugeAuth y debe extender de CBaseUserIdentity

		@return Un array de instancias de cada clase de autenticacion, solo de aquellas existentes
		en el paquete models.auth
	*/
	private function _getAuthModes(){
		if(CrugeUtil::config()->_lazyAuthModes == null){
			CrugeUtil::config()->_lazyAuthModes = array();

			// levantamos las clases directo del disco
			$ruta = Yii::getPathOfAlias('cruge.models.auth');
			Yii::log("levantando clases de autenticacion de:\n".$ruta."\n","info");
			$files = scandir($ruta);
			foreach($files as $f)
			if($f != '.' && $f != '..')
			{
				Yii::log("findfile: ".$f.", isPhp?".(CrugeUtil::isPhpFile($f)?"YES":"NO"),"info");
				if(CrugeUtil::isPhpFile($f)){
					$className = CrugeUtil::getClassNameFromPhp($f);
					if(class_exists($className)){

						if(!is_subclass_of($className, 'CBaseUserIdentity')){
							Yii::log("clase de autenticacion no es subclase de "
								."CBaseUserIdentity. clase=".$className,"error");
							throw new CrugeException(
								"clase de autenticacion no extiende de CBaseUserIdentity. ver log."
							);
						}
						// se asume que la clase provee la interfaz ICrugeAuth
						// y crea la instancia:
						$inst = new $className();

						if(self::isAuthMethodAvailable($inst->authName())==true){
							CrugeUtil::config()->_lazyAuthModes[] = $inst;
						}
						else{
							// existe, pero no esta definida en config por tanto no se agrega
							//
							Yii::log("Clase de autenticacion encontrada [{$className}] "
								."pero no esta definida en config. "
								."Su authName definido es: [".$inst->authName()."]"
								,"info");
						}
					} // endif class_exist
					else{
						Yii::log("clase de autenticacion no es valida class_exist retorna false. "
							.className,"error");
						throw new CrugeException("clase de autenticacion no es valida. ver log.");
					}
				}// endif isPhpFile
			}

			if(count(CrugeUtil::config()->_lazyAuthModes) == 0){
				//
				// si llegamos a este punto es porque no se ha configurado
				// ningun metodo de autenticacion, o porque la carpeta models.auth esta vacia
				// o ninguno de sus archivos cumple con lo necesario.
				//
				Yii::log("Ninguna clase de autenticacion declarada cumple requerimientos","error");
				throw new CrugeException("ninguna clase de autenticacion instalada "
					."cumplio requerimientos");
			}

		}// endif lazy init
		return CrugeUtil::config()->_lazyAuthModes;
	}

	/* obtiene el ICrugeStoredUser desde una sesion ICrugeSession

		porque la pongo aqui y no como una relacion de ICrugeSession directamente: para evitar una dependencia.

		@returns ICrugeStoredUser o null
	*/
	public function getSessionUser(ICrugeSession $sesion){
		if($sesion == null)
			return null;
		// no voy a hacer uso de relations() porque quiza otros ORDBM no tengan ese mecanismo
		return CrugeStoredUser::loadModel($sesion->iduser,'iduser');
	}

	/*
		crea una instancia del filtro ICrugeUserFilter instalado en el modulo,

		este metodo es referenciado en CrugeUserManager::save()

		@returns instancia que implementa a ICrugeUserFilter
	*/
	public function getUserFilter(){

		$filterClass = CrugeUtil::config()->userFilter;

		$filepath = Yii::getPathOfAlias($filterClass).".php";
		$className = CrugeUtil::getClassNameFromPhp($filepath);

		if(is_file($filepath)){
			if(!class_exists($className,false))
				require($filepath);
			if(class_exists($className,false)){
				Yii::log(__METHOD__."\nnueva instancia de: ".$className,"info");
				return new $className();
			}
			else{

				Yii::log(__METHOD__." clase no hallada.".$className,"error");
				throw new CrugeException("clase no hallada. ver log.");
			}
		}
		else
		{
			Yii::log(__METHOD__." ruta de clase es invalida:".$filepath,"error");
			throw new CrugeException(__METHOD__." ruta de clase es invalida. ver log.");
		}
	}
}